// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * MethodAssembler.h
 *
 *  Created on: Dec 2, 2009
 *      Author: dhovemey
 */

#ifndef METHODASSEMBLER_H_
#define METHODASSEMBLER_H_

#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include "vm/Opcode.h"
class Method;

/**
 * A MethodFixup is an unresolved branch target.
 * Once all of the labels have known offsets, the MethodAssembler
 * will traverse the list of fixups and insert the correct
 * offsets into the bytecode instruction sequence.
 */
struct MethodFixup {
	uint16_t offset;
	std::wstring label;

	MethodFixup(uint16_t offset_, const std::wstring &label_)
		: offset(offset_)
		, label(label_)
	{
	}
};

/**
 * A MethodAssembler helps the compiler emit bytecode for a
 * Method by allowing the use of labels to describe (possibly unknown)
 * bytecode offsets.  (This is helpful when emitting branch instructions.)
 */
class MethodAssembler {
private:
	typedef std::map<std::wstring, uint16_t> LabelMap;
	typedef std::vector<MethodFixup> FixupList;

	Method *m_method;
	LabelMap m_labelMap;
	FixupList m_fixupList;
	bool m_finished;
	int m_labelCount;
	Opcode m_lastOpcode;

	MethodAssembler(const MethodAssembler &);
	MethodAssembler& operator=(const MethodAssembler &);

public:
	/**
	 * Constructor.
	 *
	 * @param methodName name of Method to assemble
	 * @param flags      method flags
	 */
	MethodAssembler(const std::wstring &methodName, uint32_t flags);

	/** Destructor. */
	~MethodAssembler();

	/** Get the assembled Method. */
	Method *getMethod() { return m_method; }

	/**
	 * Add a parameter to the Method.
	 * @param paramName the name of the parameter to add
	 */
	void addParam(const std::wstring &paramName);

	/**
	 * Set number of local variables in the Method.
	 * This includes the receiver object ("self"),
	 * all explicit parameters, and all local variables
	 * used within the method.
	 *
	 * @param numLocals number of locals to set
	 */
	void setNumLocals(uint8_t numLocals);

	/**
	 * Emit instruction.
	 *
	 * @param opcode the Opcode of the emitted instruction
	 */
	void emitInstruction(Opcode opcode);

	/**
	 * Emit instruction.
	 *
	 * @param opcode the Opcode of the emitted instruction
	 * @param i8     signed 8-bit integer constant referenced by instruction
	 */
	void emitI8Instruction(Opcode opcode, int8_t i8);

	/**
	 * Emit instruction.
	 *
	 * @param opcode the Opcode of the emitted instruction
	 * @param cpIndex constant pool index of a Constant referenced by the instruction
	 */
	void emitCPInstruction(Opcode opcode, uint16_t cpIndex);

	/**
	 * Emit instruction.
	 *
	 * @param opcode the Opcode of the emitted instruction
	 * @param cpIndex constant pool index of a Constant referenced by the instruction
	 * @param i8     signed 8-bit integer constant referenced by instruction
	 */
	void emitCPI8Instruction(Opcode opcode, uint16_t cpIndex, int8_t i8);

	/**
	 * Generate a new undefined label.
	 * @return name of label
	 */
	std::wstring genLabel();

	/**
	 * Emit a branch instruction.
	 *
	 * @param opcode the Opcode of the emitted branch instruction
	 * @return the target label
	 */
	std::wstring emitBranchInstruction(Opcode opcode);

	/**
	 * Emit a branch instruction.
	 *
	 * @param opcode the Opcode of the emitted branch instruction
	 * @param label  the label that is targeted by the branch
	 * @return the target label
	 */
	std::wstring emitBranchInstruction(Opcode opcode, const std::wstring &label);

	/**
	 * Define given label to be the bytecode offset at the end of
	 * the previously-generated code.
	 *
	 * @param label label to define
	 */
	void defineLabel(const std::wstring &label);

	/**
	 * Get the last Opcode generated.
	 */
	Opcode getLastOpcode();

	/**
	 * Finish assembling the Method.
	 * All branches will be fixed up to reference the
	 * instructions labeled by their target labels.
	 */
	void finish();

private:
	void emitByte(int8_t b);
	void emitShort(uint16_t s);

};

#endif /* METHODASSEMBLER_H_ */
